#include "Dispatch.h"

Dispatch::Dispatch( void )
{
	socket = INVALID_SOCKET;
	data = NULL;
	length = 0;
}

Dispatch::Dispatch( DispatchFlag flag )
{
	data = NULL;
	length = 0;
	socket = INVALID_SOCKET;
	setFlag( flag );
}

Dispatch::Dispatch( DispatchFlag flag, std::string d )
{
	data = NULL;
	length = 0;
	socket = INVALID_SOCKET;
	setFlag( flag );
	setData(d.c_str(), d.size());
}

Dispatch::Dispatch( DispatchFlag flag, const char *d, size_t n )
{
	data = NULL;
	length = 0;
	socket = INVALID_SOCKET;
	setFlag( flag );
	setData(d, n);
}

Dispatch::~Dispatch()
{
	if (data != NULL) free(data);
}

bool Dispatch::release( int sock )
{
	socket = sock;
	if (socket == INVALID_SOCKET) return -1;

	// length of data plus two places for the header and length plus one for the null character.
	size_t total = getDataLength()+(sizeof(flag)+sizeof(length));
	ssize_t sent = 0;
	char buffer[total];
	char *write_ptr = buffer;
	uint16_t inc = 0;

	// *2 leave space for the length to be written in front of the data
	inc = encode_data( write_ptr, flag );
	write_ptr += inc;
	inc = encode_data( write_ptr, length );
	write_ptr += inc;

	// Append data to outgoing buffer
	memcpy(write_ptr, data, length);

	while ( total > 0 )
	{
		// Send ALL data
		sent = send(socket, buffer+sent, total-sent, 0);
		if (sent == -1)
		{
			perror("Dispatch::release: send");
//			free(buffer);
			return false;
		}
		total -= sent;
	}

//	free(buffer);

	return true;
}

ssize_t Dispatch::receive( void )
{
	if (socket == INVALID_SOCKET) return -1;

	ssize_t len = sizeof(flag) + sizeof(getDataLength());
	size_t total = 0;
	ssize_t n = 0;

	char buffer[len];
	char *read_ptr = buffer;

	// Read first two parts of packet (flag and length) -1 means an error
	len = recv( socket, buffer, len, 0 );
	if( len == sizeof(buffer) )
	{
		read_ptr += decode_data(read_ptr, &flag);
		read_ptr += decode_data(read_ptr, &len);

		char d[len+1];
		// get ALL the data
		while(total < len)// if ( total < DATA_SIZE )
		{
			n = recv( socket, d+total, len-total, 0 );
			if (n == -1) { break; }
			total += n;
		}

		d[len] = '\0';
		//fprintf(stderr, "Flag: %u, Length: %u, Data: %s\n", flag, len, data);
		setData( d, len );

	} else if (len == -1)
	{
		setFlag( SERVER_DISCONNECT );
	}

	return len+sizeof(buffer);
}

ssize_t Dispatch::receive( int s )
{
	socket = s;

	return receive();
}

void Dispatch::setFlag( DispatchFlag f )
{
	flag = f;
}
// TODO: remove management of '\0' and check code for consistancy
void Dispatch::setData( const char *d, size_t n )
{
	if (data != NULL)
		free(data);
	data = (char*)malloc(n+1);
	memcpy(data, d, n);
	data[n] = '\0';
	length = n;
}

const DispatchFlag Dispatch::getFlag( void ) const {
	return flag;
}

const std::string Dispatch::getData( void ) const {
	return data;
}

const char* Dispatch::getRawData( void ) const
{
	return data;
}

int Dispatch::getSocket( void ) const {
	return socket;
}

size_t Dispatch::getDataLength( void ) const {
	return length;
}

#ifndef __GLIBC__
	#include <stdarg.h>
	#include <cstdlib>
	int vasprintf (char **resultp, const char *format, va_list arg_list)
	{
		size_t default_size = 4096;
		size_t required_size = 4096;

	 //	va_list arg_list;
	 //	va_start (arg_list, format);

		char *result = (char*) malloc (default_size);
	//	char *result = vasnprintf (NULL, &length, format, args);
		if (result != 0)
		{
			required_size = vsnprintf (result, default_size, format, arg_list);
			if (required_size + 1 > default_size)
			{
				result = (char*) realloc (result, required_size + 1);
				if (result != 0)
					vsnprintf (result, required_size + 1, format, arg_list);
		  }
		}
		va_end (arg_list);
		if (result == NULL) {
			free (result);
			return -1;
		}

		*resultp = result;
		//Return the number of resulting bytes, excluding the trailing NUL.
		//If it wouldn't fit in an 'int', vasnprintf() would have returned NULL
		//and set errno to EOVERFLOW.
		return required_size;
	}

	int asprintf (char **resultp, const char *format, ...)
	{
		va_list args;
		int result;

		va_start (args, format);
		result = vasprintf (resultp, format, args);
		va_end (args);
		return result;
	}
#endif

